import { Router } from "express";
import { CusService } from "./CusService.js";
import { ProductService } from "../products/ProductService.js";

import { FeatureService } from "../features/FeatureService.js";
import {
  CusExpand,
  CusProductStatus,
  ErrCode,
  FullCusProduct,
  FullCustomerEntitlement,
  FullCustomerPrice,
} from "@autumn/shared";
import RecaseError, { handleFrontendReqError } from "@/utils/errorUtils.js";
import { RewardService } from "../rewards/RewardService.js";
import { EventService } from "../api/events/EventService.js";
import { createStripeCli } from "@/external/stripe/utils.js";

import { getCusEntMasterBalance } from "./entitlements/cusEntUtils.js";
import { getLatestProducts } from "../products/productUtils.js";
import { getProductVersionCounts } from "../products/productUtils.js";
import { notNullish } from "@/utils/genUtils.js";
import {
  mapToProductItems,
  mapToProductV2,
} from "../products/productV2Utils.js";
import { RewardRedemptionService } from "../rewards/RewardRedemptionService.js";
import { CusReadService } from "./CusReadService.js";
import { StatusCodes } from "http-status-codes";

export const cusRouter = Router();

// cusRouter.get("", async (req: any, res: any) => {
//   try {
//     const page = parseInt(req.query.page as string) || 1;
//     const { data: customers, count } = await CusService.getCustomers(
//       req.sb,
//       req.orgId,
//       req.env,
//       page,
//     );

//     res.status(200).json({ customers, totalCount: count });
//   } catch (error) {
//     handleFrontendReqError({ req, error, res, action: "get customers" });
//   }
// });

cusRouter.get("/:customer_id/data", async (req: any, res: any) => {
  try {
    const { db, org, features, env } = req;
    const { customer_id } = req.params;
    const orgId = req.orgId;

    const [coupons, products, customer] = await Promise.all([
      RewardService.list({
        db,
        orgId: orgId,
        env,
      }),

      ProductService.listFull({ db, orgId, env, returnAll: true }),
      CusService.getFull({
        db,
        orgId,
        env,
        idOrInternalId: customer_id,
        withEntities: true,
        expand: [CusExpand.Invoices],
        inStatuses: [
          CusProductStatus.Active,
          CusProductStatus.PastDue,
          CusProductStatus.Scheduled,
          CusProductStatus.Expired,
        ],
      }),
    ]);

    if (!customer) {
      throw new RecaseError({
        message: "Customer not found",
        code: ErrCode.CustomerNotFound,
        statusCode: StatusCodes.NOT_FOUND,
      });
    }

    let invoices = customer.invoices;
    let entities = customer.entities;
    const events = await EventService.getByCustomerId({
      db,
      internalCustomerId: customer.internal_id,
      env,
      orgId: orgId,
      limit: 10,
    });

    let fullCustomer = customer as any;
    let cusProducts = fullCustomer.customer_products;
    fullCustomer.products = fullCustomer.customer_products;
    fullCustomer.entitlements = cusProducts.flatMap(
      (product: FullCusProduct) => product.customer_entitlements,
    );
    fullCustomer.prices = cusProducts.flatMap(
      (product: FullCusProduct) => product.customer_prices,
    );

    for (const product of fullCustomer.products) {
      product.entitlements = product.customer_entitlements.map(
        (cusEnt: FullCustomerEntitlement) => {
          return cusEnt.entitlement;
        },
      );
      product.prices = product.customer_prices.map(
        (cusPrice: FullCustomerPrice) => {
          return cusPrice.price;
        },
      );
    }

    let discount = null;
    if (org.stripe_config && customer.processor?.id) {
      try {
        const stripeCli = createStripeCli({ org, env });
        const stripeCus: any = await stripeCli.customers.retrieve(
          customer.processor.id,
        );

        if (stripeCus.discount) {
          discount = stripeCus.discount;
        }
      } catch (error) {
        console.log("error", error);
      }
    }

    for (const invoice of invoices || []) {
      invoice.product_ids = invoice.product_ids.sort();
      invoice.internal_product_ids = invoice.internal_product_ids.sort();
    }

    fullCustomer.entitlements = fullCustomer.entitlements.sort(
      (a: any, b: any) => {
        const productA = fullCustomer.products.find(
          (p: any) => p.id === a.customer_product_id,
        );
        const productB = fullCustomer.products.find(
          (p: any) => p.id === b.customer_product_id,
        );

        return (
          new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ||
          b.id.localeCompare(a.id)
        );
      },
    );

    for (const cusEnt of fullCustomer.entitlements) {
      // let entitlement = cusEnt.entitlement;

      // Show used, limit, etc.
      let { balance, unused } = getCusEntMasterBalance({
        cusEnt,
        entities,
      });

      cusEnt.balance = balance;
      cusEnt.unused = unused;
    }

    res.status(200).json({
      customer: fullCustomer,
      products: getLatestProducts(products),
      versionCounts: getProductVersionCounts(products),
      invoices,
      features,
      coupons,
      events,
      discount,
      org,
      entities,
    });
  } catch (error) {
    handleFrontendReqError({ req, error, res, action: "get customer data" });
  }
});

cusRouter.get("/:customer_id/referrals", async (req: any, res: any) => {
  try {
    const { env, db } = req;
    const { customer_id } = req.params;
    const orgId = req.orgId;

    let internalCustomer = await CusService.get({
      db,
      orgId,
      env,
      idOrInternalId: customer_id,
    });

    if (!internalCustomer) {
      throw new RecaseError({
        message: "Customer not found",
        code: ErrCode.CustomerNotFound,
        statusCode: StatusCodes.NOT_FOUND,
      });
    }

    // Get all redemptions for this customer
    let [referred, redeemed] = await Promise.all([
      RewardRedemptionService.getByReferrer({
        db,
        internalCustomerId: internalCustomer.internal_id,
        withCustomer: true,
        limit: 100,
      }),
      RewardRedemptionService.getByCustomer({
        db,
        internalCustomerId: internalCustomer.internal_id,
        withReferralCode: true,
        limit: 100,
      }),
    ]);

    let redeemedCustomerIds = redeemed.map(
      (redemption: any) => redemption.referral_code.internal_customer_id,
    );

    let redeemedCustomers = await CusReadService.getInInternalIds({
      db,
      internalIds: redeemedCustomerIds,
    });

    for (const redemption of redeemed) {
      if (redemption.referral_code) {
        redemption.referral_code.customer = redeemedCustomers.find(
          (customer: any) =>
            customer.internal_id ===
            redemption.referral_code!.internal_customer_id,
        );
      }
    }

    res.status(200).send({
      referred,
      redeemed,
    });
  } catch (error) {
    handleFrontendReqError({
      req,
      error,
      res,
      action: "get customer referrals",
    });
  }
});

cusRouter.get(
  "/:customer_id/product/:product_id",
  async (req: any, res: any) => {
    try {
      const { org, env, db } = req;
      const { customer_id, product_id } = req.params;
      const { version, customer_product_id, entity_id } = req.query;
      const orgId = req.orgId;

      const customer = await CusService.getFull({
        db,
        orgId,
        env,
        idOrInternalId: customer_id,
        withEntities: true,
        entityId: entity_id,
        inStatuses: [
          CusProductStatus.Active,
          CusProductStatus.PastDue,
          CusProductStatus.Scheduled,
          CusProductStatus.Expired,
        ],
      });

      const features = await FeatureService.getFromReq(req);

      if (!customer) {
        throw new RecaseError({
          message: "Customer not found",
          code: "CUSTOMER_NOT_FOUND",
          statusCode: StatusCodes.NOT_FOUND,
        });
      }

      let cusProducts = customer.customer_products;
      let entity = customer.entity;
      let cusProduct;

      if (notNullish(customer_product_id)) {
        cusProduct = cusProducts.find(
          (p: any) =>
            p.id === customer_product_id &&
            (entity ? p.internal_entity_id === entity.internal_id : true),
        );
      } else if (notNullish(version)) {
        cusProduct = cusProducts.find(
          (p: any) =>
            p.product.id === product_id &&
            (p.status === CusProductStatus.Active ||
              p.status === CusProductStatus.PastDue) &&
            p.product.version === parseInt(version) &&
            (entity ? p.internal_entity_id === entity.internal_id : true),
        );
      } else {
        cusProduct = cusProducts.find(
          (p: any) =>
            p.product.id === product_id &&
            (p.status === CusProductStatus.Active ||
              p.status === CusProductStatus.PastDue) &&
            (entity ? p.internal_entity_id === entity.internal_id : true),
        );
      }

      let product;

      if (cusProduct) {
        let prices = cusProduct.customer_prices.map(
          (price: any) => price.price,
        );
        let entitlements = cusProduct.customer_entitlements.map(
          (ent: any) => ent.entitlement,
        );
        product = {
          ...cusProduct.product,
          items: mapToProductItems({ prices, entitlements, features }),
          free_trial: cusProduct.free_trial,
          options: cusProduct.options,
          isActive: cusProduct.status === CusProductStatus.Active,
          isCustom: cusProduct.is_custom,
        };
      } else {
        product = await ProductService.getFull({
          db,
          orgId,
          env,
          idOrInternalId: product_id,
          version:
            version && Number.isInteger(parseInt(version))
              ? parseInt(version)
              : undefined,
        });

        product = mapToProductV2({ product, features });
      }

      let numVersions = await ProductService.getProductVersionCount({
        db,
        orgId,
        env,
        productId: product_id,
      });

      // console.log("Product", product);

      res.status(200).json({
        customer,
        product,
        features,
        numVersions,
        entities: customer.entities,
      });
    } catch (error) {
      handleFrontendReqError({
        req,
        error,
        res,
        action: "get customer product",
      });
    }
  },
);
